//
//  SQLHandler.m
//  SQL演练
//
//  Created by Guo Rui on 2017/10/28.
//  Copyright © 2017年 GR Harry. All rights reserved.
//

#import "SQLHandler.h"
#import <FMDB.h>

@interface SQLHandler()
@property (strong, nonatomic) FMDatabaseQueue *dbQueue;
@property (strong, nonatomic) SQLChain *generate;
@property (strong, nonatomic) FMDatabase *db;
@end

@implementation SQLHandler

static SQLHandler *handler = nil;
+ (instancetype)sharedInstance
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        handler = [[SQLHandler alloc] init];
    });
    return handler;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.generate = [[SQLChain alloc] init];
    }
    return self;
}

#pragma mark - Public
- (void)databaseWithName:(NSString *)name
{
    NSAssert(name != nil && name.length != 0, @"path 不能为空");
    NSString *dbPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
    
    NSString *extension = [name pathExtension];
    if (!extension || !extension.length) {
        name = [name stringByAppendingString:@".sqlite"];
    }
    
    dbPath = [dbPath stringByAppendingPathComponent:name];
    dbPath = @"/Users/guorui/Desktop/haha.sqlite";
    SQLLog(@"DataBasePath : %@", dbPath);
    self.dbQueue = [FMDatabaseQueue databaseQueueWithPath:dbPath];
}

+ (void)makeSafelyHandleSQL:(SQLHandlerBlock)handle
{
    SQLHandler *sql = [self sharedInstance];
    [sql.dbQueue inDatabase:^(FMDatabase * _Nonnull db) {
        sql.db = db;
        handle(sql);
        sql.db = nil;
    }];
}

+ (void)makeTransactionHandleSQL:(SQLHandlerTransactionBlock)handle
{
    SQLHandler *sql = [self sharedInstance];
    [sql.dbQueue inTransaction:^(FMDatabase * _Nonnull db, BOOL * _Nonnull rollback) {
        sql.db = db;
        handle(sql, rollback);
        sql.db = nil;
    }];
}

#pragma mark - Private

- (SQLHandleResult *)excute
{
    NSString *sql = [self.generate sqlString];
    
    NSInteger sqlCount = [sql componentsSeparatedByString:@";"].count;
    if (sqlCount > 2) {
        // 批量操作
        [self.db beginTransaction];
        
        BOOL success = [self.db executeStatements:sql];
        success ? [self.db commit] : [self.db rollback];
        
        if (!success) SQLLog(@"excute faile.\n %@", sql);
        if (success) SQLLog(@"操作成功");
        
        [self reset];
        return [SQLHandleResult resultWithValue:nil success:success handleType:SQLHandleTypeBatch];
    }
    
    // 单条语句操作
    if ([sql hasPrefix:@"SELECT"]) {
        FMResultSet *set = [self.db executeQuery:sql];
        
        if (!set) SQLLog(@"excute faile.\n %@", sql);
        if (set) SQLLog(@"查询成功");
        
        [self reset];
        
        BOOL success = set ? YES : NO;
        return [SQLHandleResult resultWithValue:set success:success handleType:SQLHandleTypeQuery];
    }
    
    BOOL success = [self.db executeUpdate:sql];
    
    if (!success) SQLLog(@"excute faile.\n %@", sql);
    if (success) SQLLog(@"操作成功");
    
    [self reset];
    return [SQLHandleResult resultWithValue:nil success:success handleType:SQLHandleTypeUpdate];
}

- (SQLHandlerArrayParamBlock)excuteWithArgumentsInArray
{
    __weak typeof(self) weakself = self;
    
    return ^ SQLHandleResult *(NSArray *arguments){
        NSString *sql = [weakself.generate sqlString];
        
        if ([sql hasPrefix:@"SELECT"]) {
            // 查询语句
            FMResultSet *set = [weakself.db executeQuery:sql withArgumentsInArray:arguments];
            
            if (!set) SQLLog(@"excute faile.\n %@", sql);
            if (set) SQLLog(@"查询成功");
            
            [weakself reset];
            
            BOOL success = set ? YES : NO;
            return [SQLHandleResult resultWithValue:set success:success handleType:SQLHandleTypeQuery];
        }
        
        // 更新语句
        BOOL success = [weakself.db executeUpdate:sql withArgumentsInArray:arguments];
        
        if (!success) SQLLog(@"excute faile.\n %@", sql);
        if (success) SQLLog(@"操作成功");
        
        [weakself reset];
        return [SQLHandleResult resultWithValue:nil success:success handleType:SQLHandleTypeUpdate];
    };
}

- (void)reset
{
    [self.generate reset];
}
@end

@interface SQLHandleResult()
@property (assign, nonatomic, getter=isSuccess) BOOL success;
@property (strong, nonatomic) FMResultSet *value;
@property (assign, nonatomic) SQLHandleType handleType;
@end

@implementation SQLHandleResult

+ (instancetype)resultWithValue:(FMResultSet *)value success:(BOOL)success handleType:(SQLHandleType)handleType
{
    return [[self alloc] initWithValue:value success:success handleType:handleType];
}

- (instancetype)initWithValue:(FMResultSet *)value success:(BOOL)success handleType:(SQLHandleType)handleType
{
    if (self = [super init]) {
        self.value = value;
        self.success = success;
        self.handleType = handleType;
    }
    return self;
}

- (SQLHandleResultNestSetBlockBlock)enumerateSet
{
    __weak typeof(self) weakself = self;
    return ^ SQLHandleResult* (SQLHandleResultSetParamBlock block){
        if (weakself.isSuccess) {
            FMResultSet *set = weakself.value;
            while ([set next]) {
                if (block) block(set);
            }
        }
        return weakself;
    };
}

@end
